package org.simpleframework.inject;

import lombok.extern.slf4j.Slf4j;
import org.simpleframework.consts.FrameWorkConst;
import org.simpleframework.core.BeanContainer;
import org.simpleframework.inject.annotation.Autowired;
import org.simpleframework.inject.annotation.Qualifier;
import org.simpleframework.util.ClassUtil;
import org.simpleframework.util.ValidationUtil;

import java.lang.reflect.Field;
import java.util.Set;

/**
 * 实现依赖注入的类
 */
@Slf4j
public class DependencyInjector {
    // Bean容器
    private BeanContainer beanContainer;
    public DependencyInjector() {
        beanContainer = BeanContainer.getInstance();
    }

    /**
     *  执行IOC:
    */
    public void doIOC() {
        // 0) 如果beanContainer里面没有数据,则无需执行IOC
        if(beanContainer.size() == 0) {
            log.warn(FrameWorkConst.EMPTY_BEAN_CONTAINER_ERROR_MSG);
            return;
        }
        // 1) 遍历beanContainer中所有被注册进去的类
        for (Class<?> clazz : beanContainer.getClasses()) {
            // 2) 对于每一个class, 获得其所有成员变量,
            Field[] fields = clazz.getDeclaredFields();
            if(ValidationUtil.isEmpty(fields)) continue;
            // 3) 对每一个成员变量Field判断是否由@Autowired修饰, 如果是,说明要被注入
            for (Field field : fields) {
                if(field.isAnnotationPresent(Autowired.class)) {
                    // 4) 获得该field的注入对象
                    Object filedObject = getFieldInstance(field);
                    // 5) 之后通过Field对象的set方法，来实现对对应成员变量的赋值，也就是注入
                    // 如果此时filedObject还是null, 说明注入失败, 应该报错
                    if(filedObject == null) {
                        throw new RuntimeException("unable to inject relevant type，Object is: " + beanContainer.getBean(clazz) + " target fieldClass is:" + field.getType().getName() + " fieldName is : " + field.getName());
                    }
                    ClassUtil.setField(field,beanContainer.getBean(clazz),filedObject,true);
                }
            }
        }
    }

    /**
     * 获得field的注入对象
     * @param field
     * @return
     */
    private Object getFieldInstance(Field field) {
        // 1) 获得该Field对象的类型，如果容器中有该Class对象，则直接注入其实例对象；
        Class<?> type = field.getType();
        if(beanContainer.getBean(type) != null) {
            return beanContainer.getBean(type);
        }
        // 2) 如果没有，说明该类型是接口或没被之前那些注解修饰的父类，
        //    所以此时要通过容器对外提供的getClassesBySuper方法获得其实现类集合
        Set<Class<?>> classesBySuper = beanContainer.getClassesBySuper(type);
        // 3) 如果只有一个实现类, 则直接注入
        if(classesBySuper.size() == 1) {
           return beanContainer.getBean(classesBySuper.iterator().next());
        }
        // 4) 否则还要通过@Qualifier的值进行判断应该取该类型的哪一个实现类。
        Qualifier qualifier = field.getAnnotation(Qualifier.class);
        // 如果此时该field没有被@Qualifier标注,或者标注了但没有赋值, 是不合法的, 因为有多个实现类, 所以要报错
        if(qualifier == null || ValidationUtil.isEmpty(qualifier.value())) {
            throw new RuntimeException(type + FrameWorkConst.MULTIPLE_IMPLEMENTS_BUT_NOT_SET_QUALIFIER_ERROR_MSG);
        }
        String qualifierName = qualifier.value();
        for (Class<?> aClass : classesBySuper) {
            if(qualifierName.equals(aClass.getSimpleName())) {
                return beanContainer.getBean(aClass);
            }
        }
        return null;
    }
}
